local Widget = import("./Widget/Widget.lua");
local RootScope = import("./Script/RootScope.lua");

local Window = commonlib.inherit(Widget);

Window:Property("WindowName");                 -- 窗口名
Window:Property("Params");                     -- 窗口参数
Window:Property("Key");                        -- 唯一标识符
Window:Property("ZOrder", 0);                  -- z-order z轴序
Window:Property("Show", false, "IsShow");      -- 是否显示
Window:Property("G");                          -- 全局环境
Window:Property("RootScope");                  -- 根Scope
Window:Property("MouseCaptureElement");        -- 鼠标捕获元素
Window:Property("FocusElement");               -- 聚焦元素
Window:Property("Destroy", false, "IsDestroy");       -- 是否销毁

-- 鼠标捕获窗口
local __mouse_capture_window__ = nil;
-- 当前激活窗口
local __current_active_window__ = nil;
-- 窗口ID
local __window_nid__ = 1;

function Window.SetActiveWindow(win)
    __current_active_window__ = win;
end

function Window.GetActiveWindow()
    return (__current_active_window__ and not __current_active_window__:IsDestroy()) and __current_active_window__ or nil;
end

function Window.SetMouseCaptureWindow(win)
    __mouse_capture_window__ = win;
end

function Window.GetMouseCaptureWindow()
    return (__mouse_capture_window__ and not __mouse_capture_window__:IsDestroy()) and __mouse_capture_window__ or nil;
end

function Window:ctor()
    -- 窗口的位置,宽度,高度
    self.windowX, self.windowY, self.windowWidth, self.windowHeight = 0, 0, 0, 0; 
    self:SetName("Window");
    self:SetWindow(self);
    self:SetDestroy(false);
end

function Window:InitWindowPosition()
    local params = self:GetParams();
    local mainWindowWidth, mainWindowHeight = commonlib.GetWindowWidthHeight();    
    local windowX, windowY, windowWidth, windowHeight = 0, 0, tonumber(params.width) or params.width or mainWindowWidth, tonumber(params.height) or params.height or mainWindowHeight;
    local offsetX, offsetY = tonumber(params.x) or params.x or 0, tonumber(params.y) or params.y or 0;
    if (type(windowWidth) == "string" and string.match(windowWidth, "^%d+%%$")) then windowWidth = math.floor(mainWindowWidth * tonumber(string.match(windowWidth, "%d+")) / 100) end
    if (type(windowHeight) == "string" and string.match(windowHeight, "^%d+%%$")) then windowHeight = math.floor(mainWindowHeight * tonumber(string.match(windowHeight, "%d+")) / 100) end
    if (type(offsetX) == "string" and string.match(offsetX, "^%d+%%$")) then offsetX = math.floor(mainWindowWidth * tonumber(string.match(offsetX, "%d+")) / 100) end
    if (type(offsetY) == "string" and string.match(offsetY, "^%d+%%$")) then offsetY = math.floor(mainWindowHeight * tonumber(string.match(offsetY, "%d+")) / 100) end
    
    if (params.alignment == "_ctb") then                 -- *	- "_ctb": align to center bottom of the screen
        windowX, windowY = offsetX + math.floor((mainWindowWidth - windowWidth) / 2), offsetY + mainWindowHeight - windowHeight;
    elseif (params.alignment == "_ctt") then             -- *	- "_ctt": align to center top of the screen
        windowX, windowY = offsetX + math.floor((mainWindowWidth - windowWidth) / 2), offsetY;
    elseif (params.alignment == "_ctl") then             -- *	- "_ctl": align to center left of the screen
        windowX, windowY = offsetX, offsetY + math.floor((mainWindowHeight - windowHeight) / 2);
    elseif (params.alignment == "_ctr") then             -- *	- "_ctr": align to center right of the screen
        windowX, windowY = offsetX + mainWindowWidth - windowWidth , offsetY + math.floor((mainWindowHeight - windowHeight) / 2);
    elseif (params.alignment == "_lt") then              -- *	- "_lt" align to left top of the screen
        windowX, windowY = offsetX, offsetY;
    elseif (params.alignment == "_lb") then              -- *	- "_lb" align to left bottom of the screen
        windowX, windowY = offsetX, offsetY + mainWindowHeight - windowHeight;
    elseif (params.alignment == "_rt") then              -- *	- "_rt" align to right top of the screen
        windowX, windowY = offsetX + mainWindowWidth - windowWidth, offsetY;
    elseif (params.alignment == "_rb") then              -- *	- "_rb" align to right bottom of the screen
        windowX, windowY = offsetX + mainWindowWidth - windowWidth, offsetY + mainWindowHeight - windowHeight;
    elseif (params.alignment == "_mt") then              -- *	- "_mt": align to middle top
        windowX, windowY = offsetX + math.floor((mainWindowWidth - windowWidth) / 2), offsetY;
    elseif (params.alignment == "_mb") then              -- *	- "_mb": align to middle bottom
        windowX, windowY = offsetX + math.floor((mainWindowWidth - windowWidth) / 2), offsetY + mainWindowHeight - windowHeight;
    elseif (params.alignment == "_ml") then              -- *	- "_ml": align to middle left
        windowX, windowY = offsetX, offsetY + math.floor((mainWindowHeight - windowHeight) / 2);
    elseif (params.alignment == "_mr") then              -- *	- "_mr": align to middle right
        windowX, windowY = offsetX + mainWindowWidth - windowWidth, offsetY + math.floor((mainWindowHeight - windowHeight) / 2);
    elseif (params.alignment == "_fi") then              -- *	- "_fi": align to left top and right bottom. This is like fill in the parent window.
        windowX, windowY, windowWidth, windowHeight = 0, 0, mainWindowWidth, mainWindowHeight;
    else                                                 -- *	- "_ct" align to center of the screen
        windowX, windowY = offsetX + math.floor((mainWindowWidth - windowWidth) / 2), offsetY + math.floor((mainWindowHeight - windowHeight) / 2);
    end

    -- print("Window Position:", windowX, windowY, windowWidth, windowHeight, mainWindowWidth, mainWindowHeight);
    self.windowX, self.windowY, self.windowWidth, self.windowHeight = windowX, windowY, windowWidth, windowHeight;
    return self.windowX, self.windowY, self.windowWidth, self.windowHeight;
end

function Window:GetWindowPosition()
    return self.windowX, self.windowY, self.windowWidth, self.windowHeight;
end

function Window:Init(params)
    params = params or {};

    self:SetParams(params);
    self:SetWindowName(params.windowName);
    self:SetKey(params.key or params.windowName or params.url or params.template or __window_nid__);
    self:SetZOrder(params.zorder or 0);
    self:SetRootScope(RootScope(self));
    self:InitWindowPosition();

    __window_nid__ = __window_nid__ + 1;
    
    -- 子元素初始化
    Window._super.Init(self, {
        name = "window",
        attr = {
            id="window", 
            template = params.template,
            url = params.url,
            style = {
                width = self.windowWidth, 
                height = self.windowHeight,
            },
        },
    }, self, nil);

    if (type(commonlib.OnWindowCreate) == "function") then commonlib.OnWindowCreate(self) end 

    return self;
end

function Window:Refresh()
    self:GetG().__flush_newindex_scope_key_map__();
end

function Window:OnPaint()
    if (not self:IsShow()) then return end 
    self:Refresh();
    self:OnRender();

    local OnPaint = self:GetParams().OnPaint;
    if (type(OnPaint) == "function") then OnPaint(self) end
end

function Window:OnSize()
    self:InitWindowPosition();
    Window._super.Hide(self);
    Window._super.Show(self);
end

function Window:OnEvent(event)
    local event_type = event:GetEventType();
    if (event_type == "onmousedown" or event_type == "onmousemove" or event_type == "onmouseup" or event_type == "onmousewheel") then 
        self:HandleMouseEvent(event);
    else 
        self:HandleKeyEvent(event);
    end
end

-- 获取方法名通过事件名
function Window:GetEventTypeFuncName(event)
    local event_type = event:GetEventType();
    if (event_type == "onmousedown") then
        return "OnMouseDownCapture", "OnMouseDown";
    elseif (event_type == "onmouseup") then
        return "OnMouseUpCapture", "OnMouseUp";
    elseif (event_type == "onmousemove") then
        return "OnMouseMoveCapture", "OnMouseMove";
    elseif (event_type == "onmousewheel") then
        return "OnMouseWheelCapture", "OnMouseWheel";
    else 
        return "OnMouseCapture", "OnMouse";
    end
end

local __event_element_list__ = {};  -- 事件元素列表
function Window:HandleMouseEvent(event)
    local sx, sy = event:GetXY();
    local event_type = event:GetEventType();
    local target_el = self:GetMouseCaptureElement();
    local captureFuncName, bubbleFuncName = self:GetEventTypeFuncName(event);
    if (not target_el) then target_el = self:GetMouseElement(sx, sy) end 

    -- 获取事件元素连
    local size = 0;
    local el = target_el;
    commonlib.cleartable(__event_element_list__);
    while (el) do
        size = size + 1;
        __event_element_list__[size] = el;
        el = el:GetParent();
    end

    -- 捕获事件
    for i = size, 1, -1 do
        local el = __event_element_list__[i];
        if (event:IsAccept()) then break end 
        (el[captureFuncName])(el, event);
    end

    -- 冒泡事件
    for i = 1, size, 1 do
        local el = __event_element_list__[i];
        if (event:IsAccept()) then break end 
        (el[bubbleFuncName])(el, event);
    end
    
    -- 左键按下
    if (event_type == "onmousedown") then 
        if (event:IsLeftButton()) then event.__mouse_down_element__ = target_el end 
    end 
    
    -- 左键松开
    if (event_type == "onmouseup") then 
        if (event:IsLeftButton() and target_el and event.__mouse_down_element__ == target_el) then target_el:OnClick(event) end
    end 

    -- 接受事件
    event:Accept();

    self.SetActiveWindow(self);        
end

function Window:HandleKeyEvent(event)
    local event_type = event:GetEventType();
    local focus_el = self:GetFocusElement();
    if (not focus_el) then return end 
    if (event_type == "onkeydown") then
        focus_el:OnKeyDown(event);
    elseif (event_type == "onkeyup") then
        focus_el:OnKeyUp(event);
    elseif (event_type == "onchar") then
        focus_el:OnChar(event);
    else 
    end
end

function Window:Show()
    if (self:IsShow()) then return end
    self:SetShow(true);
    -- 编译元素
    self:CompileDirective();
    Window._super.Show(self);
end

function Window:Close()
    if (self:IsDestroy()) then return end
    self:SetDestroy(true);
    self:SetShow(false);
    local OnClosed = self:GetParams().OnClosed;
    if (type(OnClosed) == "function") then OnClosed(self) end 
    if (type(commonlib.OnWindowDestroy) == "function") then commonlib.OnWindowDestroy(self) end 
end

return Window;


